#pragma once

#include "my_atomic.h"

#define MY_RWLOCK_SPIN              5
#define MY_RWLOCK_WLOCK             INT64_MIN

typedef struct {
    my_atomic_int64_t   write;
    my_atomic_int64_t   counter;
} my_rwlock_t;

#define MY_RWLOCK_INITLIZER         { 0, 0 }

static inline void
my_rwlock_init(my_rwlock_t *lock)
{
    my_atomic_int64_reset(&lock->write);
    my_atomic_int64_reset(&lock->counter);
}

static inline void
my_rwlock_wlock(my_rwlock_t *lock)
{
    int64_t i = 0, j = 0;

    for ( ;; ) {
        if (my_atomic_int64_cas(&lock->write, 0, 1)) {
            my_atomic_int64_add_fetch(&lock->counter, MY_RWLOCK_WLOCK);
            for ( ;; ) {
                if (my_atomic_int64_load(&lock->counter) == MY_RWLOCK_WLOCK) {
                    return;
                }

                j++;
                if (j > MY_RWLOCK_SPIN) {
                    MY_SCHED_YIELD;
                }
            }
        }

        i++;
        if (i > MY_RWLOCK_SPIN) {
            MY_SCHED_YIELD;
        }
    }
}

static inline void
my_rwlock_wunlock(my_rwlock_t *lock)
{
    // when unlocking, it means that it has been successfully locked before.
    if (my_atomic_int64_cas(&lock->counter, MY_RWLOCK_WLOCK, 0) &&
        my_atomic_int64_cas(&lock->write, 1, 0)) {
        return;
    }

    assert(!"unexpected stats");
}

static inline void
my_rwlock_rlock(my_rwlock_t *lock)
{
    int64_t counter = 0;
    int64_t i = 0;

    for ( ;; ) {
        counter = my_atomic_int64_load(&lock->counter);
        if (counter >= 0) {
            if (my_atomic_int64_cas(&lock->counter, counter, counter + 1)) {
                return;
            }
        }

        i++;
        if (i > MY_RWLOCK_SPIN) {
            MY_SCHED_YIELD;
        }
    }
}

static inline void
my_rwlock_runlock(my_rwlock_t *lock)
{
    // when unlocking, it means that it has been successfully locked before.
    int64_t counter = 0;
    for ( ;; ) {
        counter = my_atomic_int64_load(&lock->counter);
        if (counter == MY_RWLOCK_WLOCK) {
            assert(!"unexcept state");
        }

        if (my_atomic_int64_cas(&lock->counter, counter, counter-1)) {
            return;
        }

        // no need schedule
    }
}